﻿using System.Collections.Generic;
using System.Linq;
using Cms.Application.Admin.Views;
using Cms.Application.IO;
using Cms.Data;
using Cms.Domain;
using Cms.Domain.RepositoryInterfaces;
using Cms.Utilities;

namespace Cms.Application.Admin.Presenters
{
    public class EditMediaFolderPresenter : BasePresenter
    {
        private readonly Administrator _administrator;
        private readonly IEditMediaFolderView _view;

        public IDiskAccess DiskAccess { get; set; }

        public EditMediaFolderPresenter(IEditMediaFolderView view)
            : this(view, new RepositoryFactory())
        {
            DiskAccess = new DiskAccess();
        }
        
        public EditMediaFolderPresenter(IEditMediaFolderView view, IRepositoryFactory repositoryFactory)
            : base(view, repositoryFactory)
        {
            _view = view;
            _administrator = AdministratorRepository.GetAdministrator(_view.AdministratorID);
            _view.HeadingText = GetText(_view, _view.FolderID > 0 ? "EditFolderHeading" : "NewFolderHeading");
            if (!_administrator.HasPermission.EditMediaFolder)
                _view.RedirectToPermissionDeniedPage();
        }

        public void Initialize()
        {
            InitializeParentFoldersAndSelectParentFolderID();
            MediaFolder parentFolder = MediaRepository.GetMediaFolder(_view.SelectedParentFolderID);
            MediaFolder selectedFolder = MediaRepository.GetMediaFolder(_view.FolderID);
            InitializeCategories(parentFolder, selectedFolder);
            InitializeAllowedAdministratorGroups(parentFolder, selectedFolder);

            // Set up denied administrator groups
            IList<AdministratorGroup> parentFolderDeniedAdministratorGroups = parentFolder != null
                                                                                  ? parentFolder.GetAllDeniedAdministratorGroups()
                                                                                  : new List<AdministratorGroup>();
            IList<AdministratorGroup> selectedFolderDeniedAdministratorGroups = selectedFolder != null
                                                                                    ? selectedFolder.DeniedAdministratorGroups
                                                                                    : new List<AdministratorGroup>();
            _view.SetDeniedAdministratorGroups(
                AdministratorRepository.GetCustomGroups(), 
                parentFolderDeniedAdministratorGroups, 
                selectedFolderDeniedAdministratorGroups);

            // Set up allowed user groups
            IList<UserGroup> parentFolderRequiredUserGroups = parentFolder != null 
                                                                    ? parentFolder.GetRequiredUserGroups() 
                                                                    : new List<UserGroup>();
            IList<UserGroup> selectedFolderRequiredUserGroups = selectedFolder != null 
                                                                    ? selectedFolder.RequiredUserGroups 
                                                                    : new List<UserGroup>();
            IList<UserGroup> selectedFolderDeselectedUserGroups = selectedFolder != null 
                                                                    ? selectedFolder.DeselectedUserGroups 
                                                                    : new List<UserGroup>();
            _view.SetRequiredUserGroups(
                UserRepository.GetUserGroups(), 
                parentFolderRequiredUserGroups, 
                selectedFolderRequiredUserGroups, 
                selectedFolderDeselectedUserGroups);
        }

        public void UpdateCategoriesAndGroups()
        {
            MediaFolder parentFolder = MediaRepository.GetMediaFolder(_view.SelectedParentFolderID);
            UpdateCategories(parentFolder);

            IList<AdministratorGroup> parentFolderAllowedAdministratorGroups = parentFolder != null 
                                                                                    ? parentFolder.GetAllAllowedAdministratorGroups() 
                                                                                    : new List<AdministratorGroup>();
            _view.SetAllowedAdministratorGroups(
                AdministratorRepository.GetCustomGroups(),
                parentFolderAllowedAdministratorGroups,
                _view.GetSelectedAllowedAdministratorGroups().ToList());
            IList<AdministratorGroup> parentFolderDeniedAdministratorGroups = parentFolder != null
                                                                                  ? parentFolder.GetAllDeniedAdministratorGroups()
                                                                                  : new List<AdministratorGroup>();
            _view.SetDeniedAdministratorGroups(
                AdministratorRepository.GetCustomGroups(),
                parentFolderDeniedAdministratorGroups,
                _view.GetSelectedDeniedAdministratorGroups().ToList());

            IList<UserGroup> parentFolderRequiredUserGroups = parentFolder != null
                                                                  ? parentFolder.GetRequiredUserGroups()
                                                                  : new List<UserGroup>();
            _view.SetRequiredUserGroups(
                UserRepository.GetUserGroups(),
                parentFolderRequiredUserGroups,
                _view.GetSelectedRequiredUserGroups().ToList(),
                _view.GetDeselectedUserGroups().ToList());
        }

        public bool IsInCategory(Category category)
        {
            return category.ContainsMediaFolder(MediaRepository.GetMediaFolder(_view.FolderID));
        }
        
        public bool IsInInheritedCategory(Category category)
        {
            MediaFolder folder = MediaRepository.GetMediaFolder(_view.FolderID);
            if (folder == null)
            {
                return false;
            }
            return folder.Parent != null && category.ContainsMediaFolder(folder.Parent);
        }

        public void Validate()
        {
            if (_view.Name.Trim() == string.Empty)
            {
                AddValidationError(1, GetText(_view, "NoNameError"));
            }
            else if (!RegexValidation.NameIsFileSystemCompliant(_view.Name.Trim()))
            {
                AddValidationError(2, GetText(_view, "NameNotFileSystemCompliantError"));
            }
            if (_view.FolderID > 0 && _view.SelectedParentFolderID == _view.FolderID)
            {
                AddValidationError(3, GetText(_view, "SaveInSelfError"));
            }
            if (_view.FolderID > 0)
            {
                MediaFolder editedFolder = MediaRepository.GetMediaFolder(_view.FolderID);
                MediaFolder selectedParentFolder = MediaRepository.GetMediaFolder(_view.SelectedParentFolderID);
                if (editedFolder.IsParentTo(selectedParentFolder))
                {
                    AddValidationError(4, GetText(_view, "SaveInChildError"));
                }
            }
            if (!_administrator.CurrentSite.CanMediaFolderBeNamedAndMovedTo(_view.FolderID, _view.Name, MediaRepository.GetMediaFolder(_view.SelectedParentFolderID)))
            {
                AddValidationError(5, GetText(_view, "NameCollisionError"));
            }
        }
        
        public void Save()
        {
            Validate();
            if (!IsValid)
                return;
            MediaFolder folder;
            string originalPhysicalPath = string.Empty;
            if (_view.FolderID == -1)
            {
                folder = new MediaFolder();
                folder.Site = _administrator.CurrentSite;
            }
            else
            {
                folder = MediaRepository.GetMediaFolder(_view.FolderID);
                originalPhysicalPath = folder.GetPhysicalPath();
            }
            IDictionary<int, string> originalPaths = new Dictionary<int, string>();
            CollectOriginalPathsForUpdate(folder, ref originalPaths);
            folder.Name = _view.Name;
            folder.Parent = MediaRepository.GetMediaFolder(_view.SelectedParentFolderID);

            // Clear and collect categories
            foreach (Category category in folder.Categories)
                category.MediaFolders.Remove(folder);
            folder.Categories.Clear();
            foreach (Category category in _view.GetSelectedCategories())
            {
                folder.Categories.Add(category);
                category.MediaFolders.Add(folder);
            }

            // Clear and collect allowed administrator groups
            foreach (AdministratorGroup group in folder.AllowedAdministratorGroups)
                group.AllowedMediaFolders.Remove(folder);
            folder.AllowedAdministratorGroups.Clear();
            foreach (AdministratorGroup group in _view.GetSelectedAllowedAdministratorGroups())
            {
                folder.AllowedAdministratorGroups.Add(group);
                group.AllowedMediaFolders.Add(folder);
            }

            // Clear and collect denied administrator groups
            foreach (AdministratorGroup group in folder.DeniedAdministratorGroups)
                group.DeniedMediaFolders.Remove(folder);
            folder.DeniedAdministratorGroups.Clear();
            foreach (AdministratorGroup group in _view.GetSelectedDeniedAdministratorGroups())
            {
                folder.DeniedAdministratorGroups.Add(group);
                group.DeniedMediaFolders.Add(folder);
            }

            // Clear and collect allowed user groups
            IList<UserGroup> removedUserGroups = new List<UserGroup>();
            foreach (UserGroup group in folder.RequiredUserGroups)
                removedUserGroups.Add(group);
            foreach (UserGroup group in folder.RequiredUserGroups)
                group.SelectedMediaFolders.Remove(folder);
            folder.RequiredUserGroups.Clear();
            foreach (UserGroup group in _view.GetSelectedRequiredUserGroups())
            {
                folder.RequiredUserGroups.Add(group);
                group.SelectedMediaFolders.Add(folder);
            }

            // Find out which groups that were removed
            foreach (UserGroup group in folder.RequiredUserGroups)
                if (removedUserGroups.Contains(group))
                    removedUserGroups.Remove(group);
            folder.RemoveDeselectedUserGroups(removedUserGroups);

            // Clear and collect denied user groups
            foreach (UserGroup group in folder.DeselectedUserGroups)
                group.DeselectedMediaFolders.Remove(folder);
            folder.DeselectedUserGroups.Clear();
            foreach (UserGroup group in _view.GetDeselectedUserGroups())
            {
                folder.DeselectedUserGroups.Add(group);
                group.DeselectedMediaFolders.Add(folder);
            }
            MediaRepository.SaveMediaFolder(folder);

            // Create/move the physical folder
            if (_view.FolderID == -1)
            {
                DiskAccess.CreateDirectory(folder.GetPhysicalPath());
            }
            else
            {
                DiskAccess.MoveDirectory(originalPhysicalPath, folder.GetPhysicalPath());
            }

            if (_view.FolderID == -1 || originalPhysicalPath == folder.GetPhysicalPath())
            {
                return;
            }
            var frm = new FileReferencesManager(_administrator, SiteRepository, ContentRepository, DiskAccess);
            foreach (var kvp in originalPaths)
            {
                string originalVirtualPath = kvp.Value;
                string newVirtualPath = MediaRepository.GetMediaFile(kvp.Key).GetVirtualPath();
                frm.UpdateFileReferences(originalVirtualPath, newVirtualPath, _administrator.CurrentSite.ID);
                _view.RemoveFileFromOutputCache(originalVirtualPath);
            }
        }
        
        public void CollectOriginalPathsForUpdate(MediaFolder folder, ref IDictionary<int, string> paths)
        {
            foreach (MediaFile file in folder.Files)
                paths.Add(file.ID, file.GetVirtualPath());
            foreach (MediaFolder f in folder.SubFolders)
                CollectOriginalPathsForUpdate(f, ref paths);
        }

        private void InitializeParentFoldersAndSelectParentFolderID()
        {
            _view.SetParentFolders(_administrator.CurrentSite.RootMediaFolder, _administrator.CurrentSite.GetRootMediaFolders());
            if (_view.FolderID == -1)
            {
                _view.SelectedParentFolderID = MediaRepository.GetMediaFolder(_administrator.SelectedMediaFolderID) != null
                                                   ? _administrator.SelectedMediaFolderID
                                                   : -1;
            }
            else
            {
                MediaFolder folder = MediaRepository.GetMediaFolder(_view.FolderID);
                _view.Name = folder.Name;
                if (folder.Parent == null)
                {
                    _view.SelectedParentFolderID = -1;
                }
                else
                {
                    _view.SelectedParentFolderID = folder.Parent.ID;
                }
            }
        }

        private void InitializeCategories(MediaFolder parentFolder, MediaFolder selectedFolder)
        {
            _view.SetCategories(
                _administrator.CurrentSite.GetRootCategories(),
                GetCategories(parentFolder),
                GetCategories(selectedFolder));
        }

        private void UpdateCategories(MediaFolder parentFolder)
        {
            _view.SetCategories(
                _administrator.CurrentSite.GetRootCategories(),
                GetCategories(parentFolder),
                _view.GetSelectedCategories().ToList());
        }

        private IList<Category> GetCategories(MediaFolder folder)
        {
            return folder != null ? folder.Categories : new List<Category>();
        }

        private void InitializeAllowedAdministratorGroups(MediaFolder parentFolder, MediaFolder selectedFolder)
        {
            _view.SetAllowedAdministratorGroups(
                AdministratorRepository.GetCustomGroups(),
                GetAllowedAdministratorGroups(parentFolder),
                GetAllowedAdministratorGroups(selectedFolder));
        }

        private IList<AdministratorGroup> GetAllowedAdministratorGroups(MediaFolder folder)
        {
            return folder != null ? folder.GetAllAllowedAdministratorGroups() : new List<AdministratorGroup>();
        }
    }
}